package demo.db.nacos;

import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.ConfigType;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import demo.java.lang.ThreadDemo;
import org.apache.commons.lang3.StringUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Properties;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;

/**
 * Nacos配置中心
 * https://nacos.io/docs/latest/overview/
 */
public class NacosConfigDemo {

    private static Logger logger = LoggerFactory.getLogger(NacosConfigDemo.class);
    public static final String serverAddr = "ops-nacos-nlb.jcworkint.com:8848,ops-nacos-nlb.jcworkint.com:9848";

    private ConfigService configService;


    @Before
    public void init() throws NacosException {
        Properties properties = new Properties();
        // 指定Nacos-Server的地址
        properties.setProperty(PropertyKeyConst.SERVER_ADDR, serverAddr);
        properties.setProperty(PropertyKeyConst.USERNAME, "stan.h");
        properties.setProperty(PropertyKeyConst.PASSWORD, "Stan289425433");
        // 指定Nacos-SDK的命名空间
        properties.setProperty(PropertyKeyConst.NAMESPACE, "infra-dev");

        // 初始化配置中心的Nacos Java SDK
        configService = NacosFactory.createConfigService(properties);
    }

    @After
    public void close() throws NacosException {
        configService.shutDown();
    }


    /**
     * 发布配置：
     * 用于通过程序自动发布 Nacos 配置，以便通过自动化手段降低运维成本。
     * <p>
     * 注意：创建和修改配置时使用的同一个发布接口，当配置不存在时会创建配置，当配置已存在时会更新配置。
     *
     * @param dataId  配置 ID，采用类似 package.class（如 com.taobao.tc.refund.log.level）的命名规则保证全局唯一性。
     *                建议根据配置的业务含义来定义 class 部分。全部字符均为小写。
     *                只允许英文字符和 4 种特殊字符（“.”、“:”、“-”、“_”），不超过 256 字节。
     * @param group   配置分组，建议填写产品名:模块名（如 Nacos:Test）来保证唯一性。
     *                只允许英文字符和 4 种特殊字符（“.”、“:”、“-”、“_”），不超过 128 字节。
     * @param content 配置内容，不超过 100K 字节
     * @param casMd5  前配置内容的md5
     * @param type    配置类型，见 com.alibaba.nacos.api.config.ConfigType，默认为TEXT
     * @throws NacosException
     */
    public boolean publishConfig(String dataId, String group, String content, String casMd5, ConfigType type) throws NacosException {
        // 初始化配置服务，控制台通过示例代码自动获取下面参数
        if (StringUtils.isBlank(casMd5)) {
            return configService.publishConfig(dataId, group, content, type.getType());
        } else {
            // 带Compare-And-Swap（CAS）的发布配置
            return configService.publishConfigCas(dataId, group, content, casMd5, type.getType());
        }
    }

    /**
     * 读取配置
     *
     * <li>dataId	配置 ID，采用类似 package.class（如com.taobao.tc.refund.log.level）的命名规则保证全局唯一性，class 部分建议是配置的业务含义。
     * 全部字符小写。只允许英文字符和 4 种特殊字符（”.”、”:”、”-”、”_”），不超过 256 字节。
     * <li>group	配置分组，建议填写产品名:模块名（Nacos）保证唯一性，只允许英文字符和4种特殊字符（”.”、”:”、”-”、”_”），不超过128字节。
     * <li>timeout	long	读取配置超时时间，单位 ms，推荐值 3000。
     *
     * @throws NacosException 读取配置超时或网络异常，抛出 NacosException 异常。
     */
    @Test
    public void testGetConfig() throws NacosException {
        String dataId = "infra-location-service.yaml";
        String group = "infra-location-service";
        String content = configService.getConfig(dataId, group, 3000);
        System.err.println(content);
    }

    /**
     * 监听器，配置变更进入监听器的回调函数。
     */
    @Test
    public void testListener() throws NacosException {
        String dataId = "infra-location-service.yaml";
        String group = "infra-location-service";

        Listener myListener = new Listener() {
            @Override
            public void receiveConfigInfo(String configInfo) {
                logger.info("监听到配置变更：{}", configInfo);
                System.err.println("监听到配置变更：" + configInfo);
            }

            @Override
            public Executor getExecutor() {
                return null;
            }
        };
        // 添加监听器
        configService.addListener(dataId, group, myListener);

        // 测试让主线程不退出，因为订阅配置是守护线程，主线程退出守护线程就会退出。 正式代码中无需下面代码
        ThreadDemo.safeSleep(TimeUnit.SECONDS.toMillis(60));

        System.err.println("取消监听配置，取消监听后配置不会再推送");
        configService.removeListener(dataId, group, myListener);

        // 测试让主线程不退出，因为订阅配置是守护线程，主线程退出守护线程就会退出。 正式代码中无需下面代码
        ThreadDemo.safeSleep(TimeUnit.SECONDS.toMillis(60));


    }

    @Test
    public void testGetConfigAndSignListener() throws NacosException {
        String dataId = "infra-location-service.yaml";
        String group = "infra-location-service";
        Listener myListener = new Listener() {
            @Override
            public void receiveConfigInfo(String configInfo) {
                logger.info("监听到配置变更：{}", configInfo);
                System.err.println("监听到配置变更：" + configInfo);
            }

            @Override
            public Executor getExecutor() {
                return null;
            }
        };
        // 带监听器的获取配置
        String content = configService.getConfigAndSignListener(dataId, group, 5000, myListener);
        System.err.println(content);

        // 测试让主线程不退出，因为订阅配置是守护线程，主线程退出守护线程就会退出。 正式代码中无需下面代码
        ThreadDemo.safeSleep(TimeUnit.SECONDS.toMillis(60));
    }


    @Test
    public void testPublishConfig() throws NacosException {
        String dataId = "demo.yaml";
        String group = "infra.sdk.config:datasource";
        String content = """
                spring:
                  redis:
                    host: localhost
                    port: 6379
                    password:
                """;
        boolean isOK = publishConfig(dataId, group, content, null, ConfigType.YAML);
        System.err.println("发布配置成功：" + isOK);
    }

    @Test
    public void testRemoveConfig() throws NacosException {
        String dataId = "demo.yaml";
        String group = "infra.sdk.config:datasource";
        boolean isDeleted = configService.removeConfig(dataId, group);
        System.err.println("删除配置成功：" + isDeleted);
    }


}
